/*
 * Time    : 2021-09-03 08:59:12
 * Author  : Gray Snail
 * Version : 1.0
 * Contact : shtao2011@sina.com
 * License : Copyright (c) 2021 Gray Snail
 * Desc    : 线段和多边形标注
 *
 * 操作说明:
 *
 *
 * 左击: 画线,右键完成
 * 右击: 选中,选中的框会显示锚点
 *     在选中的框上按住左键拖动,修改框的位置
 *     鼠标移动到选中标注的锚点,修改框的位置和大小
 *     按住 alt 键,左击锚点,删除锚点
 *     按住 alt 键,左击线段,添加锚点
 * 按下delete键,删除选中的标注
 */

import paper from 'paper'

import HandlePoint from './HandlePoint'
import { BaseAnno, BaseTool } from './base'
import { Cursor, KeyCode, Style } from './const'
import { checkEvent } from './utils'
import { AnnoType, ActionStat } from '../data/annotation'

const { Path } = paper

class AnnoLine extends BaseAnno {
  handlePoints = []
  constructor(points) {
    super()
    if (!(points instanceof Array)) {
      points = [points]
    }
    const line = new Path(points)
    line.strokeColor = 'yellow'
    line.strokeWidth = Style.LineWidth
    line.strokeScaling = false

    super.addChild(line)
    this.line = line
    points.forEach((pt) => this.addHandlePoint(pt))
  }

  updatePoint(point) {
    const len = this.line.segments.length
    if (len > this.handlePoints.length) {
      this.line.segments[len - 1].point = point
    } else {
      this.line.add(point)
    }
  }

  addHandlePoint(point) {
    const handlePoint = new HandlePoint({ segments: [point] })
    this.updatePoint(point)
    this.handlePoints.push(handlePoint)
    this.addChild(handlePoint)
    this.selected = true
    return handlePoint
  }

  done(closed) {
    const { line, handlePoints } = this
    if (line.segments.length > handlePoints.length) {
      line.segments[line.segments.length - 1].remove()
    }
    if (handlePoints.length > line.segments.length) {
      handlePoints.pop().remove()
    }
    const minPtNumber = closed ? 3 : 2
    if (closed) {
      this.fillColor = new paper.Color(1, 1, 1, 0.1)
    }
    this.line.closed = closed
    if (handlePoints.length < minPtNumber) {
      this.remove()
      return false
    }
    return true
  }

  movePoint(point, newPoint) {
    const { line, handlePoints } = this
    const idx = this.handlePoints.findIndex((seg) => seg.position.x == point.x && seg.position.y == point.y)
    if (idx !== -1) {
      handlePoints[idx].position = newPoint
      line.segments[idx].point = newPoint
    }
    point.x = newPoint.x
    point.y = newPoint.y
    this.triggerEvnet()
  }

  insertPoint(point, index) {
    const { line, handlePoints } = this
    const handlePoint = new HandlePoint({ segments: [point] })
    line.insert(index, point)
    handlePoint.addTo(this)
    handlePoints.splice(index, 0, handlePoint)
    console.log(line, handlePoint, index)
    this.selected = true
    this.triggerEvnet(ActionStat.UPDATE)
  }

  removePoint(point) {
    const { line, handlePoints } = this
    const index = line.segments.findIndex((item) => item.point.x === point.x && item.point.y === point.y)
    if (index > -1) {
      line.removeSegment(index)
      handlePoints[index].remove()
      handlePoints.splice(index, 1)
    }
    this.selected = true
    this.triggerEvnet(ActionStat.UPDATE)
  }

  get annoType() {
    return this.line.closed ? AnnoType.POLYGON : AnnoType.POLYLINE
  }
  get pts() {
    return this.line.segments.map((seg) => ({ x: seg.point.x, y: seg.point.y }))
  }
}

const initialLocal = () => {
  return {
    path: null,
    segment: null,
    isDraged: false,
    isModify: false,
  }
}

let local

function onMouseMove(event) {
  if (!checkEvent(event)) return
  event.tool.view.element.style.cursor = Cursor.Crosshair
  if (local.path) {
    local.path.updatePoint(event.point)
  } else {
    // 先保证元素处于选中状态
    const layer = paper.project.activeLayer
    layer.children.forEach((item) => (item.strokeWidth = Style.PointNormal))
    const hitResult = layer.hitTest(event.point, { segments: true, tolerance: 8, selected: true })
    if (hitResult) {
      hitResult.item.strokeWidth = Style.PointExpand
    }
  }
  local.segment = null
}

function onMouseDown(event) {
  if (local.path) {
    return
  }

  const selectedItem = event.tool.selectedItem
  const layer = paper.project.activeLayer
  if (event.event.altKey && event.event.buttons === 1) {
    // 已有的线删点: alt + 鼠标左键
    let hitResult = layer.hitTest(event.point, { segments: true, stroke: true, tolerance: 20, selected: true })
    if (!hitResult) {
      // do noting
    } else if (hitResult.type === 'stroke') {
      // 插入点
      selectedItem.insertPoint(hitResult.point, hitResult.location.index + 1)
      local.isModify = true
    } else if (hitResult.type === 'segment') {
      // 删除点
      selectedItem.removePoint(hitResult.point)
      local.isModify = true
    }
  } else if (event.event.buttons === 1) {
    // 选中 hanlePoint
    layer.children.forEach((item) => (item.strokeWidth = Style.PointNormal))
    const hitResult = layer.hitTest(event.point, { segments: true, tolerance: 8, selected: true })
    if (hitResult) {
      const handlePoint = hitResult.item.segments[0].point
      local.segment = { x: handlePoint.x, y: handlePoint.y }
    } else {
      local.segment = null
    }
  } else if (event.event.buttons === 2) {
    // 选中元素, 鼠标距离判断
    // layer.children.forEach((anno) => (anno.selected = false))
    const hitResult = layer.hitTest(event.point, { fill: true, segments: true, stroke: true, tolerance: 20 })
    if (hitResult) {
      if (selectedItem) {
        selectedItem.selected = false
      }
      if (hitResult.item instanceof AnnoLine) {
        hitResult.item.selected = true
        hitResult.item.fullySelected = true
        event.tool.selectedItem = hitResult.item
      } else if (hitResult.item.parent instanceof AnnoLine) {
        hitResult.item.parent.fullySelected = true
        hitResult.item.parent.selected = true
        event.tool.selectedItem = hitResult.item.parent
      }
    } else if (selectedItem) {
      selectedItem.selected = false
      event.tool.selectedItem = null
    }
  }
}

function onMouseDrag(event) {
  local.isDraged = true
  if (event.event.buttons == 2) {
    return
  }
  const selectedItem = event.tool.selectedItem
  const delta = event.delta
  if (local.segment) {
    selectedItem.movePoint(local.segment, event.point)
  } else if (selectedItem?.bounds.contains(event.point)) {
    selectedItem.translate(delta)
  }
}

function onMouseUp(event) {
  local.segment = null
  if (local.isDraged) {
    local.isDraged = false
    return
  }
  if (local.isModify) {
    local.isModify = false
    return
  }

  const selectedItem = event.tool.selectedItem
  if (selectedItem === event.item && !local.path) {
    // do nothing
  } else if (local.path) {
    if (event.event.button == 2) {
      const callback = event.tool.project.updateProperty
      const done = local.path.done(event.tool.closed)
      if (callback && done) {
        const annotation = local.path.createAnnotation()
        callback(annotation, function (params) {
          if (params) {
            console.log(params)
            event.tool.selectedItem = local.path
          } else {
            local.path.remove()
            event.tool.selectedItem = null
          }
          local.path = null
          console.log(local)
        })
      } else {
        local.path = null
      }
    } else if (event.event.button === 0) {
      const handlePt = local.path.addHandlePoint(event.point)
      handlePt.selected = true
    }
  } else if (event.event.button === 0) {
    local.path = new AnnoLine(event.point)
    local.path.selected = true
    local.path.fullySelected = true
    if (event.tool.selectedItem) {
      selectedItem.selected = false
    }
    event.tool.selectedItem = local.path
  }
}

function onKeyDown(event) {
  const selectedItem = paper.project.selectedItem
  if (event.key === KeyCode.delete && selectedItem) {
    selectedItem.remove()
    paper.project.selectedItem = null
  }
}

function onActivate(event) {
  local = initialLocal()
  const layerName = this.name + 'Layer'
  paper.projects.forEach((project) => {
    const layer = project.layers[layerName] || new paper.Layer({ name: layerName })
    project.addLayer(layer)
    layer.activate()
  })
}
function onDeactivate(event) {
  this.view.element.style.cursor = Cursor.Auto
}

export class LineTool extends BaseTool {
  constructor(options) {
    super(options)

    this.onMouseDown = onMouseDown.bind(this)
    this.onMouseDrag = onMouseDrag.bind(this)
    this.onMouseUp = onMouseUp.bind(this)
    this.onMouseMove = onMouseMove.bind(this)
    this.onActivate = onActivate.bind(this)
    this.onDeactivate = onDeactivate.bind(this)
    this.onKeyDown = onKeyDown.bind(this)
  }

  static get meta() {
    return {
      icon: 'el-icon-minus',
      desc: '画线',
    }
  }

  get meta() {
    return { ...LineTool.meta, name: this.name }
  }

  get closed() {
    return false
  }
}

export class PolyTool extends LineTool {
  static get meta() {
    return {
      icon: 'fa fa-lemon-o',
      desc: '画多边形',
    }
  }

  get meta() {
    return { ...PolyTool.meta, name: this.name }
  }

  get closed() {
    return true
  }
}

export default LineTool
